/**
  CNOK project, Anyang Normal University, IMP-CAS
  \class TAAME
  \brief nucleus mass table
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2022/03/15
  \date Last modified: 2022/03/16 by SUN Yazhou
  \copyright 2020-2023 SUN Yazhou
  \copyright CNOK project, Anyang Normal University, IMP-CAS
*/

#include <fstream>
#include <iostream>
#include "config.h"
#ifdef CATCH_CNOK
#include "catch.h"
#endif
#include <unistd.h>
#include "TAAME.h"
#include "TAMath.h"

using std::ifstream;
using std::cout;
using std::endl;

static const double u = TAMath::uMeV();
static const double EMASS = TAMath::eMeV(); // electron mass

TAAME *TAAME::fInstance = nullptr;

TAAME *TAAME::Instance(){
  if(!fInstance) fInstance = new TAAME();
  return fInstance;
} // end member function Instance

TAAME::TAAME(){
  const char *file = "config/ame21.txt";
  if(access(file, F_OK))
    TAException::Error("TAAME", "ctor: file %s does not exist.", file);
  ifstream fin(file);
  if(!fin.is_open()) TAException::Error("TAAME", "ctor: file %s open error.", file);
  const int LEN = 256; char line[LEN];
  ame_t a; int ima; // integer part of the atomic mass in u
  for(int i = 36; i--;) fin.getline(line, LEN); // skip comments
  // read the ame file //
  // cout.precision(16); // DEBUG
  while(fin.getline(line, LEN)){
    // #: unreliable data; *: uncalculable item
    for(int i = 134; i--;) if('#' == line[i] || '*' == line[i]) continue;

    sscanf(line+11, "%d%d%s", &a.z, &a.a, a.el); // Z A Element
    sscanf(line+28, "%lg%lg", &a.me, &a.dme); // mass excess in keV
    sscanf(line+56, "%lg%lg", &a.be, &a.dbe); // binding energy/A in keV
    sscanf(line+106, "%d%lg%lg", &ima, &a.ma, &a.dma); // atomic mass in u and micro-u
    if(a.z > 1) sprintf(a.name, "%d%s", a.a, a.el);
    else if(a.z == 1) sprintf(a.name, "%s", a.el); // proton
    else sprintf(a.name, "n"); // neutron
    a.ma = ima+a.ma*1.e-6;
    a.dma *= 1.e-6;

    // double ma = a.me*1.e-3/u+a.a; // DEBUG

    // cout << "Z: " << a.z << " A: " << a.a << " EL: " << a.el << endl; // DEBUG
    // cout << "mass excess: " << a.me << "(" << a.dme << ") keV" << endl; // DEBUG
    // cout << "binding energy/A: " << a.be << "(" << a.dbe << ") keV" << endl; // DEBUG
    // cout << "__mass     in u: " << a.ma << "(" << a.dma << ") u" << endl; // DEBUG
    // cout << "__mass_me  in u: " << ma << "(" << a.dme*1.e-3/u << ") u" << endl; // DEBUG

    a.ma *= u; a.dma *= u;

    // ma = a.me*1.e-3+a.a*u; // DEBUG
    // cout << "__mass    in MeV: " << a.ma << "(" << a.dma << ") MeV" << endl; // DEBUG
    // cout << "__mass_me in MeV: " << ma << "(" << a.dme*1.e-3 << ") MeV" << endl; // DEBUG
    // getchar(); // DEBUG

    if(fAME.count(a.z) && fAME[a.z].count(a.a))
      TAException::Error("TAAME", "ctor: nuclide (%d, %d) already assigned", a.z, a.a);
    fAME[a.z][a.a] = a;
  } // end while
} // end ctor

TAAME::~TAAME(){}

double TAAME::massATM(int z, int a) const{
  if(!fAME.count(z) || !fAME.at(z).count(a))
    TAException::Error("TAAME", "massATM: required nuclide (%d, %d) not registered", z, a);
  return fAME.at(z).at(a).ma;
} // end member function massATM

double TAAME::massNUC(int z, int a) const{
  return massATM(z,a) - z*EMASS; // subtract electron mass
} // end member function massNUC

const char *TAAME::name(int z, int a) const{
  if(!fAME.count(z) || !fAME.at(z).count(a))
    TAException::Error("TAAME", "name: required nuclide (%d, %d) not registered", z, a);
  return fAME.at(z).at(a).name;
} // end member function name

#ifdef CATCH_CNOK
TEST_CASE("Read Atomic Mass Evaluation File", "[ame]"){
  const TAAME *ame = TAAME::Instance();
  const double mH = ame->massATM(1, 1) / u;
  CHECK(mH == 1007825.031898e-6);
} // end TEST_CASE
#endif
